home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / dosrcss.zip / CI.C < prev    next >
C/C++ Source or Header  |  1990-07-18  |  45KB  |  1,153 lines

  1. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  2.    Distributed under license by the Free Software Foundation, Inc.
  3.  
  4. This file is part of RCS.
  5.  
  6. RCS is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. RCS is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with RCS; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. Report problems and direct all questions to:
  21.  
  22.     rcs-bugs@cs.purdue.edu
  23.  
  24. */
  25.  
  26. /*
  27.  *                     RCS checkin operation
  28.  */
  29. #ifndef lint
  30.  static char rcsid[]=
  31.  "$Header: /site/tmp/dosrcs/src/RCS/ci.c,v 5.7 90/07/16 21:33:09 lfk Release $ Purdue CS";
  32. #endif
  33. /*******************************************************************
  34.  *                       check revisions into RCS files
  35.  *******************************************************************
  36.  */
  37.  
  38.  
  39.  
  40. /* $Log:    ci.c,v $
  41.  * Revision 5.7  90/07/16  21:33:09  lfk
  42.  * test checkin to see if rcsfnms.c fixes are right
  43.  * 
  44.  * Revision 5.6  90/07/16  20:12:01  lfk
  45.  * test of '/' between pathname and filename
  46.  * 
  47.  * Revision 5.5  90/07/15  20:21:00  lfk
  48.  * Most major fixes added between rev 5.1 and rev 5.5:
  49.  *     signals fixed so they work on MS-DOS
  50.  *     Added MKS arguments code so argv can be large
  51.  *     added code to handle slashes a'la Unix
  52.  *     added more file extensions to system from MS-DOS
  53.  * 
  54.  * Revision 5.4  90/07/15  16:37:40  lfk
  55.  * Test for fixup in getwd()
  56.  * 
  57.  * Revision 5.3  90/07/15  15:59:24  lfk
  58.  * 
  59.  * This is a test
  60.  * 
  61.  * Revision 5.2  90/07/15  11:29:15  ROOT_DOS
  62.  * DOS version of RCS 4.0 checked in for MODS
  63.  * by lfk@athena.mit.edu
  64.  * Also update to MSC 6.0
  65.  * 
  66.  * revision 5.1 koya 90/01/24 06:10:56
  67.  * Initial revision
  68.  * 
  69.  * Revision 4.9  89/05/01  15:10:54  narten
  70.  * changed copyright header to reflect current distribution rules
  71.  * 
  72.  * Revision 4.8  88/11/08  13:38:23  narten
  73.  * changes from root@seismo.CSS.GOV (Super User)
  74.  * -d with no arguments uses the mod time of the file it is checking in
  75.  * 
  76.  * Revision 4.7  88/11/08  10:59:04  narten
  77.  * changes from eggert
  78.  * 
  79.  * Revision 4.7  88/08/09  19:12:07  eggert
  80.  * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one.
  81.  * Use execv(), not system(); allow cc -R; remove lint.
  82.  * isatty(fileno(stdin)) -> ttystdin()
  83.  * 
  84.  * Revision 4.6  87/12/18  11:34:41  narten
  85.  * lint cleanups (from Guy Harris)
  86.  * 
  87.  * Revision 4.5  87/10/18  10:18:48  narten
  88.  * Updating version numbers. Changes relative to revision 1.1 are actually
  89.  * relative to 4.3
  90.  * 
  91.  * Revision 1.3  87/09/24  13:57:19  narten
  92.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  93.  * warnings)
  94.  * 
  95.  * Revision 1.2  87/03/27  14:21:33  jenkins
  96.  * Port to suns
  97.  * 
  98.  * Revision 1.1  84/01/23  14:49:54  kcs
  99.  * Initial revision
  100.  * 
  101.  * Revision 4.3  83/12/15  12:28:54  wft
  102.  * ci -u and ci -l now set mode of working file properly.
  103.  * 
  104.  * Revision 4.2  83/12/05  13:40:54  wft
  105.  * Merged with 3.9.1.1: added calls to clearerr(stdin).
  106.  * made rewriteflag external.
  107.  * 
  108.  * Revision 4.1  83/05/10  17:03:06  wft
  109.  * Added option -d and -w, and updated assingment of date, etc. to new delta.
  110.  * Added handling of default branches.
  111.  * Option -k generates std. log message; fixed undef. pointer in reading of log.
  112.  * Replaced getlock() with findlock(), link--unlink with rename(),
  113.  * getpwuid() with getcaller().
  114.  * Moved all revision number generation to new routine addelta().
  115.  * Removed calls to stat(); now done by pairfilenames().
  116.  * Changed most calls to catchints() with restoreints().
  117.  * Directed all interactive messages to stderr.
  118.  * 
  119.  * Revision 3.9.1.1  83/10/19  04:21:03  lepreau
  120.  * Added clearerr(stdin) to getlogmsg() for re-reading stdin.
  121.  * 
  122.  * Revision 3.9  83/02/15  15:25:44  wft
  123.  * 4.2 prerelease
  124.  * 
  125.  * Revision 3.9  83/02/15  15:25:44  wft
  126.  * Added call to fastcopy() to copy remainder of RCS file.
  127.  *
  128.  * Revision 3.8  83/01/14  15:34:05  wft
  129.  * Added ignoring of interrupts while new RCS file is renamed;
  130.  * Avoids deletion of RCS files by interrupts.
  131.  *
  132.  * Revision 3.7  82/12/10  16:09:20  wft
  133.  * Corrected checking of return code from diff.
  134.  *
  135.  * Revision 3.6  82/12/08  21:34:49  wft
  136.  * Using DATEFORM to prepare date of checked-in revision;
  137.  * Fixed return from addbranch().
  138.  *
  139.  * Revision 3.5  82/12/04  18:32:42  wft
  140.  * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated
  141.  * field lockedby in removelock(), moved getlogmsg() before calling diff.
  142.  *
  143.  * Revision 3.4  82/12/02  13:27:13  wft
  144.  * added option -k.
  145.  *
  146.  * Revision 3.3  82/11/28  20:53:31  wft
  147.  * Added mustcheckin() to check for redundant checkins.
  148.  * Added xpandfile() to do keyword expansion for -u and -l;
  149.  * -m appends linefeed to log message if necessary.
  150.  * getlogmsg() suppresses prompt if stdin is not a terminal.
  151.  * Replaced keeplock with lockflag, fclose() with ffclose(),
  152.  * %02d with %.2d, getlogin() with getpwuid().
  153.  *
  154.  * Revision 3.2  82/10/18  20:57:23  wft
  155.  * An RCS file inherits its mode during the first ci from the working file,
  156.  * otherwise it stays the same, except that write permission is removed.
  157.  * Fixed ci -l, added ci -u (both do an implicit co after the ci).
  158.  * Fixed call to getlogin(), added call to getfullRCSname(), added check
  159.  * for write error.
  160.  * Changed conflicting identifiers.
  161.  *
  162.  * Revision 3.1  82/10/13  16:04:59  wft
  163.  * fixed type of variables receiving from getc() (char -> int).
  164.  * added include file dbm.h for getting BYTESIZ. This is used
  165.  * to check the return code from diff portably.
  166.  */
  167.  
  168. #include "rcsbase.h"
  169. #ifndef lint
  170. static char rcsbaseid[] = RCSBASE;
  171. #endif
  172. #include <sys/types.h>
  173. #include <sys/stat.h>
  174. #include "time.h"
  175.  
  176. extern int    rename();                /*rename files                       */
  177. extern char * getcaller();             /*login of caller                    */
  178. extern struct hshentry * genrevs();    /*generate delta numbers             */
  179. extern quietflag;                      /*suppresses diagnostics if true     */
  180. extern int  nerror;                    /*counter for errors                 */
  181. extern char * buildrevision();         /*constructs desired revision        */
  182. extern char * checkid();               /*check identifiers                  */
  183. extern int    partime();               /*parse free-format date/time        */
  184. extern long   maketime();              /*convert parsed time to unix time.  */
  185. extern long   time();                  /*get date and time                  */
  186. extern struct tm * localtime();        /*convert unixtime into tm-structure */
  187. extern char * getdate();               /*formates current date  (forward)   */
  188. extern char * mktempfile();            /*temporary file name generator      */
  189. extern struct lock * addlock();        /*adds a new lock                    */
  190. extern char * getlogmsg();             /*obtains log message; forward       */
  191. extern struct hshentry * removelock(); /*finds a caller's lock  (forward)   */
  192. extern struct hshentry * findlock();   /*finds a lock                       */
  193. extern char * xpandfile();             /*perform keyword expansion; forward */
  194.  
  195. extern char prevauthor[];
  196. extern char prevdate[];
  197. extern char prevrev[];
  198. extern char prevstate [];
  199. extern FILE * finptr;                  /* RCS input file                    */
  200. extern FILE * frewrite;                /* new RCS file                      */
  201. extern int    rewriteflag;             /* indicates whether input should be */
  202.                        /* echoed to frewrite                */
  203.  
  204. char * newRCSfilename, * diffilename;
  205. char * RCSfilename,*workfilename,*expfilename,*newworkfilename;
  206.  
  207. #ifdef MSDOS
  208. char tmpdir[NCPPN];
  209. extern char *gettmpdir();
  210. #endif /* MSDOS */
  211.  
  212. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  213. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  214.  
  215.  
  216. int    copyflag;    /* indicates whether a string should be copied into memory*/
  217.  
  218. char * rev, * state, *msg;
  219.  
  220. int initflag, rcsinitflag;
  221. int lockflag, keepworkingfile,keepflag;
  222. int forceciflag;                      /* forces check in                    */
  223. int symrebindflag; char * symbol;
  224. int textflag; char * textfile;
  225. char * caller;                        /* caller's login;                    */
  226. char * author;                        /* alternate author for -w option     */
  227. char altdate[datelength];             /* alternate date for -d              */
  228. int usestatdate;              /* use mod time of file for -d         */
  229. struct hshentry * targetdelta;        /* old delta to be generated          */
  230. char   * olddeltanum;                 /* number of old delta                */
  231. struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated      */
  232. char   newdelnum[revlength];          /* holds new revision number          */
  233. int    newdnumlength;                 /* actual length of new rev. num.     */
  234. char   branchpointnum[revlength];     /* number of branchpoint              */
  235. struct hshentry newdelta;             /* new delta to be inserted           */
  236. struct branchhead newbranch;          /* new branch to be inserted          */
  237. char   logmsg[logsize];               /* buffer for log message             */
  238.  
  239. #ifdef MKS
  240. main(int argc, char *argv[], char *env[])
  241. #else
  242. main (argc, argv)
  243. int argc;
  244. char * argv[];
  245. #endif /* MKS */
  246. {
  247.     char * nametest;
  248.         char * cmdusage;         /* holds command format                    */
  249.         struct stat filestatus;  /* used for getting the mode               */
  250.         int  msglen;             /* length of message given by -m           */
  251.     int exit_stats;         /* return code for command invocations     */
  252.     int newRCSmode;          /* mode for RCS file                       */
  253.     long unixtime;
  254.     struct tm  parseddate, *ftm;
  255. #ifdef MKS
  256.     int z = 0;
  257.     int ARGC = 0;
  258.     char **tmp;
  259.     char *env_name;
  260. #    define MAXARGS 500        /* This means 500 items on the command line */
  261.     if (!(tmp = (char **)malloc(sizeof(char *)*(MAXARGS+1)))) {
  262.         fprintf(stderr, "%s: can't allocate space for arguments\n",argv[0]);
  263.         exit(1);
  264.     }
  265.     for ( z = 0 ; env[z] != NULL; z++) {    /* loop through environment */
  266.         if (*env[z] == '~') {    /* testing for entries begining with '~' */
  267.             *++env[z];            /* increment pointer to delete '~' */
  268.             tmp[ARGC++] = env[z];    /* add it to our new array */
  269.             if (z >= MAXARGS) {
  270.                 fprintf(stderr, "%s: can't handle any more arguments\n", argv[0]);
  271.                 goto list;
  272.             }
  273.         }
  274.         else if (*env[z] == '_') {    /* testing for entries begining with _ */
  275.             *++env[z] ; *++env[z];    /* move past the '_' and the '=' */
  276.             env_name = env[z];    /* copy the name */
  277.         }
  278.     }
  279. list:
  280.     if ( STREQ( (char *) argv[0] , env_name ) ) {    /* test name against startup args */
  281.         /* environment arguments meant for this program */
  282. #    ifdef DEBUG
  283.         printf("Using shell supplied args\n");
  284. #    endif
  285.         argc = ARGC;
  286.         tmp[ARGC] = NULL;    /* the terminal NULL */
  287.         argv = tmp;
  288.     }
  289. #    ifdef DEBUG
  290.     else 
  291.         printf("Using startup supplied args\n");
  292. #    endif /* debug */
  293. #endif /* MKS */
  294.     catchints();
  295.         cmdid = "ci";
  296.         cmdusage = "command format:\nci -r[rev] -l[rev] -u[rev] -f[rev] -k[rev] -q[rev] -mmsg -nname -Nname -sstate -t[txtfile] file ...";
  297.     rev = state = msg = symbol = textfile = nil;
  298.         initflag= rcsinitflag= symrebindflag= textflag= quietflag= false;
  299.         forceciflag= lockflag= keepworkingfile= keepflag= false;
  300.     caller = getcaller(); author = nil; /* author may be reset by -w */
  301.     altdate[0]= '\0'; /* empty alternate date for -d */
  302.     usestatdate=false;
  303.  
  304.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  305.                 switch ((*argv)[1]) {
  306.  
  307.                 case 'r':
  308.                         lockflag=false;
  309.                 revno:  if ((*argv)[2]!='\0') {
  310.                                 if (rev!=nil) warn("Redefinition of revision number");
  311.                                 rev = (*argv)+2;
  312.                         }
  313.                         break;
  314.  
  315.                 case 'l':
  316.                         keepworkingfile=lockflag=true;
  317.                         goto revno;
  318.  
  319.                 case 'u':
  320.                         keepworkingfile=true; lockflag=false;
  321.                         goto revno;
  322.  
  323.                 case 'q':
  324.                         quietflag=true;
  325.                         goto revno;
  326.  
  327.                 case 'f':
  328.                         forceciflag=true;
  329.                         goto revno;
  330.  
  331.                 case 'k':
  332.                         keepflag=true;
  333.                         goto revno;
  334.  
  335.                 case 'm':
  336.                         if ((*argv)[2]!='\0'){
  337.                                 if (msg!=nil)warn("Redefinition of -m option");
  338.                                 msg = (*argv)+2;
  339.                                 msglen=strlen(msg);
  340.                                 if (msglen >= logsize) {
  341.                                    warn("log message truncated to %d characters",
  342.                                         logsize);
  343.                                    msg[logsize-2]='\n';
  344.                                    msg[logsize-1]='\0';
  345.                                 }
  346.                                 if (msg[msglen-1]!='\n') {
  347.                                    /*append linefeed*/
  348.                                    VOID strcpy(logmsg,msg);msg=logmsg;
  349.                                    msg[msglen]  = '\n';
  350.                                    msg[++msglen]= '\0';
  351.                                 }
  352.                         } else warn("Missing message for -m option");
  353.                         break;
  354.  
  355.                 case 'n':
  356.                         symrebindflag=false;
  357.                         if ((*argv)[2]!='\0'){
  358.                                 if (symbol!=nil)warn("Redefinition of symbolic name");
  359.                                 symbol = (*argv)+2;
  360.                 if (!(nametest=checkid(symbol,' '))||*nametest)
  361.                     faterror("Name %s must be one word",symbol);
  362.                         } else warn("Missing name for -n option");
  363.                         break;
  364.  
  365.                 case 'N':
  366.                         symrebindflag=true;
  367.                         if ((*argv)[2]!='\0'){
  368.                                 if (symbol!=nil)warn("Redefinition of symbolic name");
  369.                                 symbol = (*argv)+2;
  370.                 if (!(nametest=checkid(symbol,' '))||*nametest)
  371.                     faterror("Name %s must be one word",symbol);
  372.                         } else warn("Missing name for -N option");
  373.                         break;
  374.  
  375.                 case 's':
  376.                         if ((*argv)[2]!='\0'){
  377.                                 if (state!=nil)warn("Redefinition of -s option");
  378.                                 state = (*argv)+2;
  379.                                 VOID checkid(state,' ');
  380.                         } else warn("Missing state for -s option");
  381.                         break;
  382.  
  383.                 case 't':
  384.                         textflag=true;
  385.                         if ((*argv)[2]!='\0'){
  386.                                 if (textfile!=nil)warn("Redefinition of -t option");
  387.                                 textfile = (*argv)+2;
  388.                         }
  389.                         break;
  390.  
  391.         case 'd':
  392.                         if ((*argv)[2]!='\0'){
  393.                 if (altdate[0]!='\0' || usestatdate==true)
  394.                     warn("Redefinition of -d option");
  395.                 /* process the date */
  396.                 if ( partime((*argv)+2, &parseddate) == 0) {
  397.                     faterror("Can't parse date/time: %s", (*argv)+2);
  398.                     break;
  399.                 }
  400.                 if ( (unixtime = maketime(&parseddate)) == 0L) {
  401.                     faterror("Inconsistent date/time: %s",(*argv)+2);
  402.                     break;
  403.                 }
  404.                 ftm = localtime(&unixtime);
  405.                 VOID sprintf(altdate,DATEFORM,
  406.                 ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  407.             } else 
  408.                 usestatdate++;
  409.                         break;
  410.  
  411.         case 'w':
  412.                         if ((*argv)[2]!='\0'){
  413.                 if (author!=nil)warn("Redefinition of -w option");
  414.                 author = (*argv)+2;
  415.                 VOID checkid(author,' ');
  416.             } else warn("Missing author for -w option");
  417.                         break;
  418.  
  419.  
  420.  
  421.  
  422.                 default:
  423.                         faterror("unknown option: %s\n%s", *argv,cmdusage);
  424.                 };
  425.         }  /* end processing of options */
  426.  
  427.         if (argc<1) faterror("No input file\n%s",cmdusage);
  428.  
  429.         if (!ttystdin() && msg==nil && textflag && textfile==nil) {
  430.                 /* would need both log message and descriptive text from a file */
  431.                 faterror("Can't take both log and description from redirected stdin; use -ttextfile");
  432.         }
  433.         /* now handle all filenames */
  434. #ifdef MSDOS
  435.     strcpy( tmpdir, gettmpdir());
  436. #endif /* MSDOS */
  437.         do {
  438.         gendeltas[0] = nil;
  439.         copyflag=rewriteflag=false;
  440.         finptr=frewrite=NULL;
  441.         targetdelta=nil;
  442.         olddeltanum=nil;
  443.  
  444.         switch (pairfilenames(argc,argv,false,false)) {
  445.  
  446.         case -1:                /* New RCS file */
  447.                 initflag=true; rcsinitflag=false;
  448.                 break;
  449.  
  450.         case 0:                 /* Error */
  451.                 continue;
  452.  
  453.         case 1:                 /* Normal checkin with prev . RCS file */
  454.                 initflag=false; rcsinitflag=(Head==nil);
  455.         }
  456.  
  457.         /* now RCSfilename contains the name of the RCS file, and
  458.          * workfilename contains the name of the working file.
  459.          * if !initflag, finptr contains the file descriptor for the
  460.          * RCS file. The admin node is initialized.
  461.          * workstat and RCSstat are set.
  462.          */
  463.  
  464.         diagnose("%s  <--  %s", RCSfilename,workfilename);
  465.  
  466.         if (access(workfilename,4)!=0) {
  467.                 error("working file %s not readable or nonexistent",
  468.                        workfilename);
  469.                 continue;
  470.         }
  471.  
  472.         /*
  473.          * make sure workfile is a regular file.
  474.          */
  475.         VOID stat(workfilename, &filestatus);
  476.         if ((filestatus.st_mode & S_IFMT) != S_IFREG) {
  477.                 error("working file %s isn't a regular file", workfilename);
  478.                 continue;
  479.         }
  480.  
  481.         /*
  482.          * if RCSfile doesn't exist, use mode from workfile, otherwise
  483.          * keep the one from the RCSfile.
  484.          */
  485.         if (! (initflag || rcsinitflag))
  486.             VOID fstat(fileno(finptr), &filestatus);
  487.  
  488.         if (!trydiraccess(RCSfilename)) continue; /* give up */
  489.         if (!initflag && !checkaccesslist(caller))   continue; /* give up */
  490.         if (!trysema(RCSfilename,true)) continue; /* give up */
  491.  
  492.         if (keepflag) {
  493.                 /* get keyword values from working file */
  494.                 if (!getoldkeys(workfilename)) continue;
  495.                 if (rev==nil && *(rev=prevrev)=='\0') {
  496.                         error("Can't find a revision number in %s",workfilename);
  497.                         continue;
  498.                 }
  499.         if (*prevdate=='\0' && *altdate=='\0' && usestatdate==false)
  500.             warn("Can't find a date in %s",workfilename);
  501.         if (*prevauthor=='\0' && author==nil)
  502.                         warn("Can't find an author in %s", workfilename);
  503.         if (*prevstate=='\0' && state==nil)
  504.                         warn("Can't find a state in %s", workfilename);
  505.         } /* end processing keepflag */
  506.  
  507.         gettree(); /* reads in the delta tree.*/
  508.  
  509.         /* expand symbolic revision number */
  510.         if (!expandsym(rev,newdelnum)) continue;
  511.  
  512.         /* splice new delta into tree */
  513.         if (!addelta()) continue;
  514.  
  515.         if (initflag||rcsinitflag) {
  516.                 diagnose("initial revision: %s",newdelnum);
  517.         } else  diagnose("new revision: %s; previous revision: %s",
  518.                 newdelnum,olddeltanum);
  519.  
  520.         newdelta.num=newdelnum;
  521.         newdelta.branches=nil;
  522.         newdelta.log=nil;
  523.         newdelta.lockedby=nil; /*might be changed by addlock() */
  524.     /* set author */
  525.     if (author!=nil)
  526.         newdelta.author=author;     /* set author given by -w         */
  527.     elsif (keepflag && *prevauthor!='\0')
  528.         newdelta.author=prevauthor; /* preserve old author of possible*/
  529.     else    newdelta.author=caller;     /* otherwise use caller's id      */
  530.     if (state!=nil)
  531.         newdelta.state=state;       /* set state given by -s          */
  532.     elsif (keepflag && *prevstate!='\0')
  533.         newdelta.state=prevstate;   /* preserve old state if possilbe */
  534.     else    newdelta.state=DEFAULTSTATE;/* otherwise use default state    */
  535.     if (usestatdate==true) {
  536.         if(haveworkstat<0) {
  537.         error("can't stat %s",workfilename);
  538.         continue;
  539.         } 
  540.         ftm = localtime(&workstat.st_mtime);
  541.         VOID sprintf(altdate,DATEFORM,ftm->tm_year,ftm->tm_mon+1,
  542.         ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  543.     }
  544.     if (*altdate!='\0')
  545.         newdelta.date=altdate;      /* set date given by -d           */
  546.     elsif (keepflag && *prevdate!='\0') /* preserve old date if possible  */
  547.                 newdelta.date  =prevdate;
  548.     else
  549.         newdelta.date = getdate();  /* use current date               */
  550.     /* now check validity of date -- needed because of -d and -k          */
  551.     if (targetdelta!=nil &&
  552.         cmpnum(newdelta.date,targetdelta->date)<=0) {
  553.         error("Date %s is not later than %s in existing revision %s",
  554.                newdelta.date,targetdelta->date, targetdelta->num);
  555.         continue;
  556.     }
  557.  
  558.  
  559.         if (lockflag && !addlock(&newdelta,caller)) continue;
  560.         if (symbol && !addsymbol(&newdelta,symbol,symrebindflag)) continue;
  561.  
  562.         /* prepare for rewriting the RCS file */
  563.         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  564.         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  565.                 error("Can't open file %s",newRCSfilename);
  566.                 continue;
  567.         }
  568.         putadmin(frewrite);
  569.         puttree(Head,frewrite);
  570.         putdesc(initflag,textflag,textfile,quietflag);
  571.  
  572.  
  573.         /* build rest of file */
  574.         if (initflag||rcsinitflag) {
  575.                 /* get logmessage */
  576.                 newdelta.log=getlogmsg();
  577.                 if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
  578.                 ffclose(frewrite); frewrite=NULL;
  579.         } else {
  580. #ifdef MSDOS
  581.                 diffilename=mktempfile(tmpdir,DIFFILE);
  582. #else
  583.                 diffilename=mktempfile("/tmp/",DIFFILE);
  584. #endif /* MSDOS */
  585.                 if (&newdelta==Head) {
  586.                         /* prepend new one */
  587.                         rewriteflag=false;
  588.                         if (!(expfilename=
  589. #ifdef MSDOS
  590.                               buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
  591. #else
  592.                               buildrevision(gendeltas,targetdelta,"/tmp/",false))) continue;
  593. #endif /* MSDOS */
  594.                         if (!mustcheckin(expfilename,targetdelta)) continue;
  595.                                 /* don't check in files that aren't different, unless forced*/
  596.                         newdelta.log=getlogmsg();
  597.                         exit_stats = run((char*)nil, diffilename,
  598. #ifdef MSDOS
  599. #    ifdef GNUDIFF
  600.                 DIFF,"-a","-n",workfilename,expfilename, (char*)nil);
  601. #    elif MKS
  602.                 DIFF,"-n",workfilename,expfilename, (char*)nil);
  603. #    endif
  604. #else
  605.                 DIFF,"-n",workfilename,expfilename, (char*)nil);
  606. #endif /* MSDOS */
  607. #ifdef MSDOS
  608.                         if (exit_stats != 0 && exit_stats != 1 )
  609. #else
  610.                         if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
  611. #endif /* MSDOS */
  612.                             faterror ("diff failed");
  613.                         /* diff returns 2 in the upper byte on failure */
  614.                         if(!putdtext(newdelnum,newdelta.log,workfilename,frewrite)) continue;
  615.                         if(!putdtext(olddeltanum,targetdelta->log,diffilename,frewrite)) continue;
  616.                 } else {
  617.                         /* insert new delta text */
  618.                         rewriteflag=true;
  619.                         if (!(expfilename=
  620. #ifdef MSDOS
  621.                               buildrevision(gendeltas,targetdelta,tmpdir,false))) continue;
  622. #else
  623.                               buildrevision(gendeltas,targetdelta,"/tmp/",false))) continue;
  624. #endif /* MSDOS */
  625.                         if (!mustcheckin(expfilename,targetdelta)) continue;
  626.                                 /* don't check in files that aren't different, unless forced*/
  627.                         newdelta.log=getlogmsg();
  628.                         exit_stats = run((char*)nil, diffilename,
  629. #ifdef MSDOS
  630. #ifdef GNUDIFF
  631.                 DIFF,"-a","-n",expfilename,workfilename, (char*)nil);
  632. #    elif MKS
  633.                 DIFF,"-n",expfilename,workfilename, (char*)nil);
  634. #    endif
  635. #else
  636.                 DIFF,"-n",expfilename,workfilename, (char*)nil);
  637. #endif /* MSDOS */
  638. #ifdef MSDOS
  639.                         if (exit_stats != 0 && exit_stats != 1 )
  640. #else
  641.                         if (exit_stats != 0 && exit_stats != (1 << BYTESIZ))
  642. #endif /* MSDOS */
  643.                             faterror ("diff failed");
  644.                         if(!putdtext(newdelnum,newdelta.log,diffilename,frewrite)) continue;
  645.                 }
  646.  
  647.                 /* rewrite rest of RCS file */
  648.                 fastcopy(finptr,frewrite);
  649.                 ffclose(frewrite); frewrite=NULL;
  650.         }
  651.     ignoreints();
  652. #ifdef MSDOS
  653.         if (Rename(newRCSfilename,RCSfilename)<0) {
  654. #else
  655.         if (rename(newRCSfilename,RCSfilename)<0) {
  656. #endif /* MSDOS */
  657.                 error("Can't write new RCS file %s; saved in %s",
  658.                       RCSfilename,newRCSfilename);
  659.                 newRCSfilename[0]='\0'; /* avoid deletion by cleanup*/
  660.                 restoreints();
  661.                 VOID cleanup();
  662.                 break;
  663.         }
  664.         newRCSfilename[0]='\0'; /* avoid re-unlinking by cleanup()*/
  665.  
  666. #ifdef MSDOS
  667.     newRCSmode= (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~S_IWRITE;
  668. #else
  669.     newRCSmode= (initflag|rcsinitflag?workstat.st_mode:RCSstat.st_mode)& ~0222;
  670. #endif /* MSDOS */
  671.     /* newRCSmode is also used to adjust mode of working file for -u and -l */
  672.     if (chmod(RCSfilename,newRCSmode)<0)
  673.                 warn("Can't set mode of %s",RCSfilename);
  674.  
  675.         restoreints();
  676. #       ifdef SNOOPFILE
  677.         logcommand("ci",&newdelta,gendeltas,caller);
  678. #       endif
  679.  
  680.         if (!keepworkingfile) {
  681.                 VOID unlink(workfilename); /* get rid of old file */
  682.         } else {
  683.                 /* expand keywords in file */
  684.                 newworkfilename=
  685.                 xpandfile(workfilename,workfilename /*for directory*/,&newdelta);
  686.                 if (!newworkfilename) continue; /* expand failed */
  687.         ignoreints();
  688. #ifdef MSDOS
  689.         if (Rename(newworkfilename,workfilename) <0) {
  690. #else
  691.         if (rename(newworkfilename,workfilename) <0) {
  692. #endif /* MSDOS */
  693.                     error("Can't expand keywords in %s",workfilename);
  694.                     restoreints();
  695.                     continue;
  696.                 }
  697.         newworkfilename[0]='\0'; /* avoid re-unlink by cleanup */
  698.         if (chmod(workfilename, WORKMODE(newRCSmode))<0)
  699.                     warn("Can't adjust mode of %s",workfilename);
  700.                 restoreints();
  701.         }
  702.         diagnose("done");
  703.  
  704.         } while (cleanup(),
  705.                  ++argv, --argc >=1);
  706.  
  707.         exit(nerror!=0);
  708.     /*NOTREACHED*/
  709. }       /* end of main (ci) */
  710. /*****************************************************************/
  711. /* the rest are auxiliary routines                               */
  712.  
  713.  
  714. int addelta()
  715. /* Function: Appends a delta to the delta tree, whose number is
  716.  * given by newdelnum[]. Updates Head, newdelnum, newdenumlength,
  717.  * olddeltanum and the links in newdelta.
  718.  * Retruns false on error, true on success.
  719.  */
  720. {
  721.         register char * sp, * tp;
  722.         register int i;
  723.  
  724.         newdnumlength=countnumflds(newdelnum);
  725.  
  726.         if (initflag || rcsinitflag ) {
  727.                 /* this covers non-existing RCS file and a file initialized with rcs -i */
  728.         if ((newdnumlength==0)&&(Dbranch!=nil)) {
  729.             VOID strcpy(newdelnum,Dbranch->num);
  730.             newdnumlength=countnumflds(newdelnum);
  731.         }
  732.                 if (newdnumlength==0) VOID strcpy(newdelnum,"1.1");
  733.                 elsif (newdnumlength==1) VOID strcat(newdelnum,".1");
  734.                 elsif (newdnumlength>2) {
  735.                     error("Branch point does not exist for %s",newdelnum);
  736.                     return false;
  737.                 } /* newdnumlength == 2 is OK;  */
  738.                 olddeltanum=nil;
  739.                 Head = &newdelta;
  740.                 newdelta.next=nil;
  741.                 return true;
  742.         }
  743.         if (newdnumlength==0) {
  744.                 /* derive new revision number from locks */
  745.         targetdelta=findlock(caller,true); /*find and delete it*/
  746.                 if (targetdelta) {
  747.                     /* found an old lock */
  748.                     olddeltanum=targetdelta->num;
  749.                     /* check whether locked revision exists */
  750.                     if (!genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas)) return false;
  751.                     if (targetdelta==Head) {
  752.                         /* make new head */
  753.                         newdelta.next=Head;
  754.                         Head= &newdelta;
  755.                         incnum(olddeltanum, newdelnum);
  756.                     } elsif ((targetdelta->next==nil)&&(countnumflds(olddeltanum)>2)) {
  757.                         /* new tip revision on side branch */
  758.                         targetdelta->next= &newdelta;
  759.                         newdelta.next = nil;
  760.                         incnum(olddeltanum, newdelnum);
  761.                     } else {
  762.                         /* middle revision; start a new branch */
  763.                         newdelnum[0]='\0';
  764.                         if (!addbranch(targetdelta,newdelnum)) return false;
  765.                     }
  766.                     return true; /* successfull use of existing lock */
  767.                 } else {
  768.                     /* no existing lock; try Dbranch */
  769.                     /* update newdelnum */
  770. #ifdef MSDOS
  771.                     if (!(StrictLocks==false) ) {
  772. #else
  773.                     if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
  774. #endif /* MSDOS */
  775.                         error("no lock set by %s",caller);
  776.                         return false;
  777.                     }
  778.                     if (Dbranch) {
  779.                         VOID strcpy(newdelnum,Dbranch->num);
  780.                     } else {
  781.                         incnum(Head->num,newdelnum);
  782.                     }
  783.                     newdnumlength=countnumflds(newdelnum);
  784.                     /* now fall into next statement */
  785.                 }
  786.         }
  787.         if (newdnumlength<=2) {
  788.                 /* add new head per given number */
  789.                 olddeltanum=Head->num;
  790.                 if(newdnumlength==1) {
  791.                     /* make a two-field number out of it*/
  792.                     if (cmpnumfld(newdelnum,olddeltanum,1)==0)
  793.                           incnum(olddeltanum,newdelnum);
  794.                     else  VOID strcat(newdelnum, ".1");
  795.                 }
  796.                 if (cmpnum(newdelnum,olddeltanum) <= 0) {
  797.                     error("deltanumber %s too low; must be higher than %s",
  798.                           newdelnum,Head->num);
  799.                     return false;
  800.                 }
  801.                 if (!(targetdelta=removelock(caller,Head))) return false;
  802.                 if (!(genrevs(olddeltanum,(char *)nil,(char *)nil,(char *)nil,gendeltas))) return false;
  803.                 newdelta.next=Head;
  804.                 Head= &newdelta;
  805.         } else {
  806.                 /* put new revision on side branch */
  807.                 /*first, get branch point */
  808.                 tp=branchpointnum; sp=newdelnum;
  809.                 for(i=newdnumlength-(newdnumlength%2==1?1:2);i>0;i--) {
  810.                     while (*sp != '.') *tp++ = *sp++; /*copy field*/
  811.                     *tp++ = *sp++;                    /*copy dot  */
  812.                 }
  813.                 *(tp-1) = '\0'; /* kill final dot */
  814.                 olddeltanum=branchpointnum; /*temporary old delta*/
  815.                 if (!(targetdelta=genrevs(branchpointnum,(char *)nil,(char *)nil,(char *)nil,gendeltas)))
  816.                      return false;
  817.                 if (cmpnum(targetdelta->num,branchpointnum)!=0) {
  818.                     error("Cannot find branchpoint %s",branchpointnum);
  819.                     return false;
  820.                 }
  821.                 if (!addbranch(targetdelta,newdelnum)) return false;
  822.         }
  823.         return true;
  824. }
  825.  
  826.  
  827.  
  828. int addbranch(branchpoint,num)
  829. struct hshentry * branchpoint;
  830. char * num;
  831. /* adds a new branch and branch delta at branchpoint.
  832.  * If num is the null string, appends the new branch, incrementing
  833.  * the highest branch number (initially 1), and setting the level number to 1.
  834.  * the new delta and branchhead are in globals newdelta and newbranch, resp.
  835.  * the new number is placed into num.
  836.  * returns false on error.
  837.  */
  838. {
  839.         struct branchhead * bhead, * btrail;
  840.         char branchnum[revlength];
  841.         int numlength, result, field;
  842.  
  843.         numlength = countnumflds(num);
  844.  
  845.         if (branchpoint->branches==nil) {
  846.                 /* start first branch */
  847.                 branchpoint->branches = &newbranch;
  848.                 if (numlength==0) {
  849.                         VOID strcpy(num, branchpoint->num);
  850.                         VOID strcat(num,".1.1");
  851.                 } elsif(countnumflds(num)%2 == 1)
  852.                         VOID strcat(num, ".1");
  853.                 newbranch.nextbranch=nil;
  854.  
  855.         } elsif (numlength==0) {
  856.                 /* append new branch to the end */
  857.                 bhead=branchpoint->branches;
  858.                 while (bhead->nextbranch) bhead=bhead->nextbranch;
  859.                 bhead->nextbranch = &newbranch;
  860.                 getbranchno(bhead->hsh->num,branchnum);
  861.                 incnum(branchnum,num);
  862.                 VOID strcat(num,".1");
  863.                 newbranch.nextbranch=nil;
  864.         } else {
  865.                 /* place the branch properly */
  866.                 field = numlength - (numlength%2 ==1?0:1);
  867.                 /* field of branch number */
  868.                 bhead=branchpoint->branches;
  869.                 while ((bhead!=nil) &&
  870.                        ((result=cmpnumfld(num,bhead->hsh->num,field))>0)) {
  871.                         btrail=bhead;
  872.                         bhead=bhead->nextbranch;
  873.                 }
  874.                 if (bhead==nil || result<0) {
  875.                         /* insert/append new branchhead */
  876.                         if (bhead==branchpoint->branches)
  877.                                 branchpoint->branches= &newbranch;
  878.                         else    btrail->nextbranch= &newbranch;
  879.                         newbranch.nextbranch=bhead;
  880.                         if (numlength%2 ==1) VOID strcat(num,".1");
  881.                 } else {
  882.                         /* branch exists; append to end */
  883.                         getbranchno(num,branchnum);
  884.                         if (!(targetdelta=genrevs(branchnum,(char *)nil,(char *)nil,(char *)nil,
  885.                                 gendeltas))) return false;
  886.                         olddeltanum=targetdelta->num;
  887.                         if (cmpnum(num,olddeltanum) <= 0) {
  888.                                 error("deltanumber %s too low; must be higher than %s",
  889.                                       num,olddeltanum);
  890.                                 return false;
  891.                         }
  892.                         if (!removelock(caller,targetdelta)) return false;
  893.                         if (numlength%2==1) incnum(olddeltanum,num);
  894.                         targetdelta->next= &newdelta;
  895.                         newdelta.next=nil;
  896.                         return true; /* Don't do anything to newbranch */
  897.                 }
  898.         }
  899.         newbranch.hsh = &newdelta;
  900.         newdelta.next=nil;
  901.         return true;
  902. }
  903.  
  904.  
  905.  
  906. struct hshentry * removelock(who,delta)
  907. char * who; struct hshentry * delta;
  908. /* function: Finds the lock held by who on delta,
  909.  * removes it, and returns a pointer to the delta.
  910.  * Prints an error message and returns nil if there is no such lock.
  911.  * An exception is if StrictLocks==false, and who is the owner of
  912.  * the RCS file. If who does not have a lock in this case,
  913.  * delta is returned.
  914.  */
  915. {
  916.         register struct lock * next, * trail;
  917.         char * num;
  918.         struct lock dummy;
  919.         int whomatch, nummatch;
  920.  
  921.         num=delta->num;
  922.         dummy.nextlock=next=Locks;
  923.         trail = &dummy;
  924.         while (next!=nil) {
  925.                 whomatch=strcmp(who,next->login);
  926.                 nummatch=strcmp(num,next->delta->num);
  927.                 if ((whomatch==0) && (nummatch==0)) break;
  928.                      /*found a lock on delta by who*/
  929.                 if ((whomatch!=0)&&(nummatch==0)) {
  930.                     error("revision %s locked by %s",num,next->login);
  931.                     return nil;
  932.                 }
  933.                 trail=next;
  934.                 next=next->nextlock;
  935.         }
  936.         if (next!=nil) {
  937.                 /*found one; delete it */
  938.                 trail->nextlock=next->nextlock;
  939.                 Locks=dummy.nextlock;
  940.                 next->delta->lockedby=nil; /* reset locked-by */
  941.                 return next->delta;
  942.         } else {
  943. #ifdef MSDOS
  944.                 if (!(StrictLocks==false) ) {
  945. #else
  946.                 if (!((StrictLocks==false) && (getuid() == RCSstat.st_uid))) {
  947. #endif /* MSDOS */
  948.                     error("no lock set by %s for revision %s",who,num);
  949.                     return nil;
  950.                 } else {
  951.                         return delta;
  952.                 }
  953.         }
  954. }
  955.  
  956.  
  957.  
  958. char * getdate()
  959. /* Function: returns a pointer to the current date in the form
  960.  * YY.MM.DD.hh.mm.ss\0
  961.  */
  962. {
  963.         long clock;
  964.         struct tm * tm;
  965.     static char buffer[datelength]; /* date buffer */
  966.  
  967.         clock=time((long *)0);
  968.         tm=localtime(&clock);
  969.         VOID sprintf(buffer, DATEFORM,
  970.                 tm->tm_year, tm->tm_mon+1, tm->tm_mday,
  971.                 tm->tm_hour, tm->tm_min, tm->tm_sec);
  972.         return buffer;
  973. }
  974.  
  975.  
  976. char * xpandfile (unexfname,dir,delta)
  977. char * unexfname, * dir;
  978. struct hshentry * delta;
  979. /* Function: Reads file unexpfname and copies it to a
  980.  * file in dir, performing keyword substitution with data from delta.
  981.  * returns the name of the expanded file if successful, nil otherwise.
  982.  */
  983. {       char * targetfname;
  984.         FILE * unexfile, *exfile;
  985.  
  986.         targetfname=mktempfile(dir,TMPFILE3);
  987.         if ((unexfile=fopen(unexfname,  "r" ))==NULL ||
  988.             (exfile  =fopen(targetfname,"w"))==NULL) {
  989.                 error("Can't expand file %s",unexfname);
  990.                 return nil;
  991.         }
  992.         while (expandline(unexfile,exfile,delta,false,false)); /*expand*/
  993.         ffclose(unexfile);ffclose(exfile);
  994.         return targetfname;
  995. }
  996.  
  997.  
  998. mustcheckin (unexfname,delta)
  999. char * unexfname; struct hshentry * delta;
  1000. /* Function: determines whether checkin should proceed.
  1001.  * Compares the wrkfilename with unexfname, disregarding keywords.
  1002.  * If the 2 files differ, returns true. If they do not differ, asks the user
  1003.  * whether to return true or false (i.e., whether to checkin the file anyway.
  1004.  * If the files do not differ, and quietflag==true, returns false.
  1005.  * Shortcut: If forceciflag==true, mustcheckin() always returns true.
  1006.  */
  1007. {       register int c;
  1008.         int response, result;
  1009.  
  1010.         if (forceciflag) return true;
  1011.  
  1012.         if (!rcsfcmp(workfilename,unexfname,delta)) return true;
  1013.         /* If files are different, must check them in. */
  1014.  
  1015.         /* files are the same */
  1016.         diagnose("File %s is unchanged with respect to revision %s",
  1017.                 workfilename,delta->num);
  1018.         if (quietflag || !ttystdin()) {
  1019.                 /* Files are the same, but can't ask, so don't checkin*/
  1020.                 result=false;
  1021.         } else {
  1022.                 /* ask user whether to check in */
  1023.                 VOID fputs("checkin anyway? [ny](n): ",stderr);
  1024.                 response=c=getchar();
  1025.                 while (!(c==EOF || c=='\n')) c=getchar();/*skip to end of line*/
  1026.                 result=(response=='y'||response=='Y');
  1027.         }
  1028.         if (result==false) {
  1029.                 if (quietflag) {
  1030.                     warn("checkin aborted since %s was not changed; %s %sdeleted.",
  1031.                              workfilename,workfilename,keepworkingfile?"not ":"");
  1032.                 } else {
  1033.                     diagnose("checkin aborted; %s %sdeleted.",
  1034.                              workfilename,keepworkingfile?"not ":"");
  1035.                 }
  1036.                 if (!keepworkingfile) VOID unlink(workfilename);
  1037.         }
  1038.         return result;
  1039. }
  1040.  
  1041.  
  1042.  
  1043.  
  1044. /* --------------------- G E T L O G M S G --------------------------------*/
  1045. extern int stdinread; /* is >0 if redirected stdin has been read once.     */
  1046.  
  1047.  
  1048. char * getlogmsg()
  1049. /* Function: obtains a log message and returns a pointer to it.
  1050.  * If a log message is given via the -m option, a pointer to that
  1051.  * string is returned.
  1052.  * If this is the initial revision, a standard log message is returned.
  1053.  * Otherwise, reads a character string from the terminal.
  1054.  * The string must be terminated with a control-d or a single '.' on a
  1055.  * line. getlogmsg prompts the first time it is called for the
  1056.  * log message; during all later calls it asks whether the previous
  1057.  * log message can be reused.
  1058.  * returns a pointer to the character string; the pointer is always non-nil.
  1059.  */
  1060. {
  1061.         static logyet;      /*indicates whether previous log present*/
  1062.         static char emptylog[]  = "*** empty log message ***\n";
  1063.         static char initiallog[]= "Initial revision\n";
  1064.         char response;
  1065.     int cin;
  1066.         register char c, old1, old2, * tp;
  1067.  
  1068.         if (msg) return msg;
  1069.  
  1070.         if ((olddeltanum==nil)&&
  1071.         ((cmpnum(newdelnum,"1.1")==0)||(cmpnum(newdelnum,"1.0")==0))) {
  1072.                 return initiallog;
  1073.     }
  1074.     if (keepflag) {
  1075.         /* generate std. log message */
  1076.         VOID sprintf(logmsg, "checked in with -k by %s at %s.\n",caller,getdate());
  1077.         return(logmsg);
  1078.     }
  1079.         if (logyet) {
  1080.                 /*previous log available*/
  1081.                 if (!ttystdin()) return logmsg; /* reuse if stdin is not a terminal*/
  1082.                 /* otherwise ask */
  1083.         clearerr(stdin);        /* reset EOF ptr */
  1084.         VOID fputs("reuse log message of previous file? [yn](y): ",stderr);
  1085.                 cin=getchar();
  1086.         response=cin;
  1087.                 while (!(cin==EOF || cin=='\n')) cin=getchar();/*skip to end of line*/
  1088.                 if (response=='\n'||response=='y'||response=='Y')
  1089.                         return logmsg;
  1090.                 else
  1091.                         logmsg[0]='\0'; /*kill existing log message */
  1092.         }
  1093.  
  1094.         /* now read string from stdin */
  1095.         if (ttystdin()) {
  1096. #ifdef MSDOS
  1097.                 VOID fputs("enter log message:\n(terminate with ^Z or single '.')\n>> ",stderr);
  1098. #else
  1099.                 VOID fputs("enter log message:\n(terminate with ^D or single '.')\n>> ",stderr);
  1100. #endif /* MSDOS */
  1101.         } else {  /* redirected stdin */
  1102.                 if (stdinread>0)
  1103.                     faterror("Can't reread redirected stdin for log message; use -m");
  1104.                 stdinread++;
  1105.         }
  1106.  
  1107.     tp=logmsg; old1='\n'; old2=' ';
  1108.     if (feof(stdin))
  1109.         clearerr(stdin);
  1110.         for (;;) {
  1111.                 cin=getchar();
  1112.                 if (cin==EOF) {
  1113.             if(ttystdin()) {
  1114.                 VOID printf("\n");
  1115.                 clearerr(stdin);
  1116.             }
  1117.             if ((tp==logmsg)||(*(tp-1)!='\n')) *tp++ = '\n'; /* append newline */
  1118.                         *tp = '\0'; /*terminate*/
  1119.                         break;
  1120.                 }
  1121.                 if (cin=='\n' && old1=='.' && old2=='\n') {
  1122.                         *(tp-1) = '\0'; /*kill last period */
  1123.                         break;
  1124.                 }
  1125.                 if (tp>=logmsg+logsize-2) { /* overflow */
  1126.                         if (!ttystdin()) {
  1127.                                 warn("log message truncated to %d characters",logsize);
  1128.                                 logmsg[logsize-2]='\n';logmsg[logsize-1]='\0';
  1129.                                 return logmsg;
  1130.                         }
  1131.                         VOID fprintf(stderr,"log message too long. Maximum: %d\n",logsize);
  1132.                         VOID fputs("reenter log message:\n>> ",stderr);
  1133.                         tp=logmsg; old1='\n'; old2=' ';
  1134.                         while (cin!='\n') cin=getchar(); /*skip line */
  1135.                         continue;
  1136.                 }
  1137.                 if (cin=='\n' && ttystdin()) VOID fputs(">> ",stderr);
  1138.                 *tp++ = cin; old2=old1; old1=cin; /* this is the actual work!*/
  1139.                 /*SDELIM will be changed to double SDELIM by putdtext*/
  1140.         } /* end for */
  1141.  
  1142.         /* now check whether the log message is not empty */
  1143.         tp=logmsg;
  1144.         while ((c= *tp++)==' '||c=='\t'||c=='\n'||c=='\f');
  1145.         if (*tp=='\0') {
  1146.                 logyet=false;
  1147.                 return emptylog;
  1148.         } else {
  1149.                 logyet=true;
  1150.                 return logmsg;
  1151.         }
  1152. }
  1153.